﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DistributedGraph
{
    /// <summary>
    /// Stellt die Menge von Kantentypen dar.
    /// </summary>
    public enum LinkType
    {
        Undirected,
        Directed
    }

    /// <summary>
    /// Bildet eine Kante ab.
    /// </summary>
    public class Link
    {
        #region Konstanten
        private static int NEXTID = 0;
        private static object syncObject = new object();
        #endregion Konstanten

        #region Attribute
        private int id;
        private Node sourceNode;
        private Node targetNode;
        private LinkType linkType;
        #endregion Attribute

        #region Eigenschaften
        /// <summary>
        /// Gibt die ID der Kante an.
        /// </summary>
        public int Id
        {
            get { return id; }
        }

        /// <summary>
        /// Gibt den Typen der Kante an.
        /// </summary>
        public LinkType LinkType
        {
            get { return linkType; }
        }

        /// <summary>
        /// Gibt den Ausgangsknoten der Kante an.
        /// </summary>
        public Node SourceNode
        {
            get { return sourceNode; }
        }

        /// <summary>
        /// Gibt den Zielknoten der Kante an.
        /// </summary>
        public Node TargetNode
        {
            get { return targetNode; }
        }
        #endregion Eigenschaften

        #region Methoden
        /// <summary>
        /// Instanziiert und initialisiert die Klasse.
        /// </summary>
        /// <param name="linkType">Der Typ der Kante.</param>
        /// <param name="sourceNode">Der Ausgangsknoten der Kante.</param>
        /// <param name="targetNode">Der Zielknoten der Kante.</param>
        public Link(LinkType linkType, Node sourceNode, Node targetNode)
        {
            if (sourceNode == null) throw new ArgumentNullException("sourceNode");
            if (targetNode == null) throw new ArgumentNullException("targetNode");
            lock (syncObject)
            {
                this.id = NEXTID++;
            }
            this.linkType = linkType;
            this.sourceNode = sourceNode;
            this.targetNode = targetNode;
            sourceNode.AddLink(this);
            if (sourceNode != targetNode) targetNode.AddLink(this);
        }

        /// <summary>
        /// Setzt die Id zurück. (Nur anwenden wenn keine Instanzen mehr existieren!)
        /// </summary>
        public static void ResetID()
        {
            NEXTID = 1;
        }

        /// <summary>
        /// Zur Debugausgabe der bezeichnenden Zeichenkette.
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Enum.GetName(typeof(LinkType), linkType) + " Kante #" + id.ToString() + " von Knoten #" + sourceNode.Id.ToString() + " zu Knoten  #" + targetNode.Id.ToString();
        }

        /// <summary>
        /// Transportiert eine Nachricht.
        /// </summary>
        /// <param name="sender">Der Sender der Nachricht.</param>
        /// <param name="message">Die Nachricht.</param>
        internal void Transport(Node sender, IMessage message)
        {
            if (linkType == DistributedGraph.LinkType.Directed && sourceNode != sender) throw new Exception("Der Sender kann nicht über diese Kante senden.");
            if (linkType == DistributedGraph.LinkType.Undirected && (sourceNode != sender && targetNode != sender)) throw new Exception("Der Sender kann nicht über diese Kante senden.");
            (sourceNode == sender ? targetNode : sourceNode).Receive(this, message);
        }
        #endregion Methoden
    }
}
